package blackstar.agent.schedule;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

import blackstar.agent.event.Event;
import blackstar.agent.event.EventListener;
import blackstar.agent.event.ShutdownEvent;
import blackstar.agent.event.StartupEvent;

/**
 * @author RayWoo (ayufox@gmail.com)
 * 
 */
public class QuartzScheduleService implements EventListener
{
	private final static Log LOGGER = LogFactory
			.getLog(QuartzScheduleService.class);

	private final Scheduler scheduler;

	private int seq = 0;

	private synchronized int getNextSeq()
	{
		seq++;
		return seq;
	}

	public QuartzScheduleService() throws SchedulerException
	{
		scheduler = StdSchedulerFactory.getDefaultScheduler();
	}

	public void onEvent(Event event) throws Exception
	{
		if (event instanceof StartupEvent)
		{
			startup();
		} else if (event instanceof ShutdownEvent)
		{
			shutdown();
		} else if (event instanceof ScheduleEvent)
		{
			if (!scheduler.isShutdown())
			{
				ScheduleEvent se = (ScheduleEvent) event;
				if (se.getId() == null)
				{
					schedule(se.getCronExpression(), se.getTask());
				} else
				{
					schedule(se.getId(), se.getCronExpression(), se.getTask());
				}
			}
		} else if (event instanceof UnscheduleEvent)
		{
			if (!scheduler.isShutdown())
			{
				UnscheduleEvent ue = (UnscheduleEvent) event;
				unschedule(ue.getId());
			}
		} else
		{
			LOGGER.warn("Error Event [" + event + "]");
		}
	}

	public void startup() throws SchedulerException
	{
		if (!scheduler.isStarted())
		{
			scheduler.start();
		}
	}

	public void shutdown() throws SchedulerException
	{
		if (!scheduler.isShutdown())
		{
			scheduler.shutdown();
		}
	}

	public void schedule(String expression, ScheduleTask task) throws Exception
	{
		schedule("default" + getNextSeq(), expression, task);
	}

	public void schedule(String name, String cronExpression, ScheduleTask task)
			throws Exception
	{
		LOGGER.info("schedule[name=" + name + ",cron=" + cronExpression
				+ ",task=" + task + "]");
		JobDetail jobDetail = new JobDetail(name + "Job",
				Scheduler.DEFAULT_GROUP, ScheduleJob.class);
		jobDetail.getJobDataMap().put("task", task);

		CronTrigger trigger = new CronTrigger(name + "Trigger",
				Scheduler.DEFAULT_GROUP, cronExpression);

		scheduler.scheduleJob(jobDetail, trigger);
	}

	public void unschedule(String name) throws Exception
	{
		scheduler.deleteJob(name + "Job", Scheduler.DEFAULT_GROUP);
	}

	public Class[] events()
	{
		return new Class[]
		{ StartupEvent.class, ShutdownEvent.class, ScheduleEvent.class,
				UnscheduleEvent.class };
	}

	public static class ScheduleJob implements Job
	{
		public void execute(JobExecutionContext context)
				throws JobExecutionException
		{
			ScheduleTask task = (ScheduleTask) context.getJobDetail()
					.getJobDataMap().get("task");
			try
			{
				task.schedule();
			} catch (Exception e)
			{
				LOGGER.error("error when run task[" + task + "]", e);
			}
		}
	}

}
